﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SpeedSearch
{
    public partial class Form1 : Form
    {
        private ConcurrentBag<FileInfos> _resultatsRecherche = new ConcurrentBag<FileInfos>();
        private IEnumerable<FileInfos> _resultatsRechercheVirtuelle;
        private VirtualTree _virtualization;
        private int _directoriesCount = 0;
        private ListViewPerso listView1;
        private DateTime start;
        private DateTime stop;

        // Gestion de la partie virtualisation
        private VirtualizationWrapper _virtualizationGestor;

        // Options pour le parallelisme
        private ParallelOptions parallelOptions;
        private CancellationTokenSource cancelTokenSource;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            listView1 = new ListViewPerso();
            CreateMyListView();

            Config.GetInstance().LoadConfig();
        }

        private void CreateMyListView()
        {
            // Create a new ListView control.

            //listView1.Bounds = new Rectangle(new Point(10, 10), new Size(300, 200));

            // Set the view to show details.
            listView1.View = View.Details;
            // Allow the user to edit item text.
            listView1.LabelEdit = true;
            // Allow the user to rearrange columns.
            listView1.AllowColumnReorder = true;
            // Display check boxes.
            listView1.CheckBoxes = false;
            // Select the item and subitems when selection is made.
            listView1.FullRowSelect = true;
            // Display grid lines.
            listView1.GridLines = true;
            // Sort the items in the list in ascending order.
            listView1.Sorting = SortOrder.Ascending;

            // Create three items and three sets of subitems for each item.
            //ListViewItem item1 = new ListViewItem("Aucun résultat", 0);

            // Create columns for the items and subitems.
            listView1.Columns.Add(new ColHeader("Nom", 100, HorizontalAlignment.Left, true));
            listView1.Columns.Add(new ColHeader("Extension", 100, HorizontalAlignment.Left, true));
            listView1.Columns.Add(new ColHeader("Emplacement", 100, HorizontalAlignment.Left, true));
            listView1.Columns.Add(new ColHeader("Taille (KB)", 100, HorizontalAlignment.Left, true));
            listView1.Columns.Add(new ColHeader("Dernière modification", 120, HorizontalAlignment.Left, true));

            //Add the items to the ListView.
            //listView1.Items.Add(item1);

            // Add the ListView to the control collection.
            listView1.Dock = DockStyle.Fill;
            this.panelList.Controls.Add(listView1);

            // Add context menu strip
            this.splitContainer1.ContextMenuStrip = cmsResultats;

            // Connect the ListView.ColumnClick event to the ColumnClick event handler.
            //this.listView1.ColumnClick += new ColumnClickEventHandler(listView1.PersoColumnClick);
            this.listView1.DoubleClick += new EventHandler(listView1_DoubleClick);
        }

        private void listView1_DoubleClick(object sender, EventArgs e)
        {
            if (this.listView1.SelectedItems.Count > 0)
                OpenSelectedFile();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            _resultatsRecherche = new ConcurrentBag<FileInfos>();
            _resultatsRechercheVirtuelle = null;
            _directoriesCount = 0;

            // mise en place des options de parallelisme
            parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = System.Environment.ProcessorCount * 2;
            cancelTokenSource = new CancellationTokenSource();
            parallelOptions.CancellationToken = cancelTokenSource.Token;

            // mise en place des paramètres de recherche
            SearchParams Params = new SearchParams
            {
                RootSearchPath = tbEmplacementRacine.Text,
                SearchPattern = tbFichierDossierExtension.Text,
                ContainsText = tbContient.Text,
                RealTimeResults = cbTpsReel.Checked,
                IncludesSubDirectories = cbSubRep.Checked,
                Virtualization = cbVirtualisation.Checked,
                VirtualizedSearch = (_virtualization != null && _virtualization.RootName == tbEmplacementRacine.Text),
                CaseSensitive = cbCaseSensitive.Checked
            };

            // en cas de virtualisation demandée, l'ensemble l'ensemble de l'arborescence est mappée
            // cela accélère énormément les recherches, mais l'utilisation de la mémoire est importante
            if (Params.Virtualization)
            {
                e.Result = "Virtualisation";
                start = DateTime.Now;
                Virtualize(Params);
                stop = DateTime.Now;
            }
            else
            {
                e.Result = (Params.VirtualizedSearch) ? "Recherche virtuelle" : "Recherche physique";
                start = DateTime.Now;

                // demande d'initialisation de la listeView
                listView1.RefreshInvoke(listView1.RefreshDelegate, new FileInfos { ClearFlag = true }, Params.RealTimeResults);

                // recherche aynchrone
                Search(Params);

                // demande d'affichage complet de la listeView
                if (!Params.Virtualization && _resultatsRecherche.Count > 0 && !Params.RealTimeResults)
                    listView1.RefreshInvoke(listView1.RefreshAllDelegate, _resultatsRecherche);
                else if (Params.VirtualizedSearch)
                    listView1.RefreshInvoke(listView1.RefreshAllDelegate, _resultatsRechercheVirtuelle);

                stop = DateTime.Now;
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            switch ((string)e.Result)
            {
                case "Virtualisation":
                    MessageBox.Show(String.Format("Virtualisation terminée. Nombre de dossiers scannés : {0}. Durée de la virtualisation : {1}",
                _directoriesCount, stop - start));
                    break;
                case "Recherche virtuelle":
                    MessageBox.Show(String.Format("Recherche terminée. Nombre de dossiers scannés : {0} - Résultats : {1}. Durée de la recherche : {2}",
                        _directoriesCount, _resultatsRechercheVirtuelle.Count(), stop - start));
                    break;
                case "Recherche physique":
                    MessageBox.Show(String.Format("Recherche terminée. Nombre de dossiers scannés : {0} - Résultats : {1}. Durée de la recherche : {2}",
                        _directoriesCount, _resultatsRecherche.Count, stop - start));
                    break;
                default:
                    break;
            }
            btnArreter.Enabled = false;
            btnRecherche.Enabled = true;
        }

        private void Virtualize(SearchParams aParams)
        {
            var newParams = new SearchParams
            {
                ContainsText = "",
                IncludesSubDirectories = true,
                RealTimeResults = false,
                RootSearchPath = aParams.RootSearchPath,
                SearchPattern = "",
                Virtualization = true,
                VirtualizedSearch = false,
                CaseSensitive = false
            };
            _virtualization = new VirtualTree(aParams.RootSearchPath);
            Search(newParams);
            _virtualization.DirectoriesCount = _directoriesCount;
        }

        private bool Search(SearchParams aParams)
        {
            if (backgroundWorker1.CancellationPending || parallelOptions.CancellationToken.IsCancellationRequested)
                return false;
            else
            {
                if (aParams.RootSearchPath != "" && !aParams.VirtualizedSearch)
                {
                    try
                    {
                        // recherche de la cible dans le répertoire racine ...
                        IEnumerable<FileInfo> priorSearch = null;
                        if (!aParams.Virtualization)
                            priorSearch = new DirectoryInfo(aParams.RootSearchPath).EnumerateFiles(aParams.SearchPattern);
                        else
                            priorSearch = new DirectoryInfo(aParams.RootSearchPath).EnumerateFiles();

                        Interlocked.Increment(ref _directoriesCount);

                        // parcours des fichiers énumérés
                        if (priorSearch.Count() > 0)
                        {
                            SearchFilesInDirectory(aParams, priorSearch);
                            if (backgroundWorker1.CancellationPending)
                                return false;
                        }

                        // découpage des répertoires sur des threads cloisonnés
                        if (aParams.IncludesSubDirectories)
                        {
                            SearchDirectoriesInDirectory(aParams);
                            if (backgroundWorker1.CancellationPending)
                                return false;
                        }
                    }
                    catch
                    {

                    }
                }
                // Cas de recherche dans l'arbrescence virtuelle
                else if (aParams.RootSearchPath != "" &&
                    aParams.VirtualizedSearch)
                {
                    // identification et application des pattern de recherche
                    var pattern = aParams.SearchPattern.Split('*').ToList();
                    var appliedResults = SearchPatternsFilter(_virtualization.FilesInfos, pattern);

                    // retrait des valeurs nulles
                    var _virtualizationTemp = _virtualization.FilesInfos.Where(x => x != null);

                    // application de la recherche de contenu
                    _resultatsRechercheVirtuelle = _virtualizationTemp.Where(x => x.FileContents.Contains(aParams.ContainsText));
                }
                return true;
            }
        }

        private void SearchDirectoriesInDirectory(SearchParams aParams)
        {
            var directoriesEnum = new DirectoryInfo(aParams.RootSearchPath).EnumerateDirectories();

            Parallel.ForEach(directoriesEnum, parallelOptions, (dir) =>
            {
                if (parallelOptions.CancellationToken.IsCancellationRequested)
                    return;
                else
                    SearchDirectoriesInDirectoryInternal(dir, aParams);
            });
        }

        private void SearchDirectoriesInDirectoryInternal(DirectoryInfo dir, SearchParams aParams)
        {
            if (!parallelOptions.CancellationToken.IsCancellationRequested)
            {
                Interlocked.Increment(ref _directoriesCount);
                if (!Search(new SearchParams
                {
                    RootSearchPath = dir.FullName,
                    SearchPattern = aParams.SearchPattern,
                    ContainsText = aParams.ContainsText,
                    RealTimeResults = aParams.RealTimeResults,
                    IncludesSubDirectories = aParams.IncludesSubDirectories,
                    Virtualization = aParams.Virtualization,
                    VirtualizedSearch = aParams.VirtualizedSearch,
                    CaseSensitive = aParams.CaseSensitive
                }))
                    return;
            }
            else
                return;
        }

        private void SearchFilesInDirectory(SearchParams aParams, IEnumerable<FileInfo> priorSearch)
        {
            Parallel.ForEach(priorSearch, parallelOptions, (item) =>
                {
                    if (parallelOptions.CancellationToken.IsCancellationRequested)
                        return;
                    else
                        SearchFilesInDirectoryInternal(item, aParams);
                });
        }

        private void SearchFilesInDirectoryInternal(FileInfo item, SearchParams aParams)
        {
            if (!parallelOptions.CancellationToken.IsCancellationRequested)
            {
                FileInfos fileInfos = new FileInfos
                {
                    FilePath = item.FullName,
                    FileName = item.Name,
                    FileRoot = item.DirectoryName,
                    FileExtension = item.Extension,
                    FileSize = item.Length,
                    FileLastUpdated = item.LastWriteTime
                };

                // si virtualisation, on stocke également tout le contenu
                if (aParams.Virtualization)
                {
                    using (var fileParse = new FileParsing(fileInfos.FilePath))
                    {
                        fileInfos.FileContents = fileParse.GetContents();
                    }

                    // enlistage des FileInfos
                    _virtualization.FilesInfos.Enqueue(fileInfos);
                }

                // recherche de pattern dans le fichier
                if (aParams.ContainsText != "")
                {
                    using (var fileParsing = new FileParsing(item.FullName) { StringToSearch = aParams.ContainsText })
                    {
                        if (fileParsing.IsFileContainingString(aParams.CaseSensitive))
                        {
                            _resultatsRecherche.Add(fileInfos);
                            // demande de rafraîchissement de la liste
                            if (aParams.RealTimeResults)
                                listView1.RefreshInvoke(listView1.RefreshDelegate, fileInfos, true);
                        }
                    }
                }
                else if (aParams.ContainsText == "" && !aParams.Virtualization)
                {
                    _resultatsRecherche.Add(fileInfos);
                    // demande de rafraîchissement de la liste
                    if (aParams.RealTimeResults)
                        listView1.RefreshInvoke(listView1.RefreshDelegate, fileInfos, true);
                }
            }
            else
                return;
        }

        private IEnumerable<FileInfos> SearchPatternsFilter(IEnumerable<FileInfos> startEnumerable, List<string> searchPattern)
        {
            IEnumerable<FileInfos> tempresult = startEnumerable;
            foreach (var item in searchPattern)
            {
                if (item != "")
                {
                    tempresult = startEnumerable.Where(x => x.FileName.Contains(item));
                }
            }

            return startEnumerable;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                tbEmplacementRacine.Text = folderBrowserDialog1.SelectedPath;
        }

        private void emplacementToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listView1.Items.Count > 0)
                Clipboard.SetText(listView1.SelectedItems[0].SubItems[2].Text, TextDataFormat.Text);
        }

        private void ouvrirLeFichierToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenSelectedFile();
        }

        private void OpenSelectedFile()
        {
            if (listView1.Items.Count > 0)
            {
                Process newProcess = new Process();
                newProcess.StartInfo.Arguments = listView1.SelectedItems[0].SubItems[2].Text + @"\" + listView1.SelectedItems[0].SubItems[0].Text;
                newProcess.StartInfo.FileName = "notepad++";

                try
                {
                    newProcess.Start();
                }
                catch
                {
                    MessageBox.Show("notepad++ n'est pas installé sur votre machine ... pas bien !");
                    newProcess.StartInfo.FileName = "notepad";
                    newProcess.Start();
                }
            }
        }

        private void btnRecherche_Click_1(object sender, EventArgs e)
        {
            btnArreter.Enabled = true;
            btnRecherche.Enabled = false;
            backgroundWorker1.RunWorkerAsync();
        }

        private void btnArreter_Click_1(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
            cancelTokenSource.Cancel();
        }

        private void btnVirtualisation_Click(object sender, EventArgs e)
        {
            tabControlRecherches.Hide();
            if (_virtualizationGestor == null)
                _virtualizationGestor = new VirtualizationWrapper(splitContainer1.Panel1, this.imageList1);
            else
                _virtualizationGestor.Show();
        }

        private void btnRecherches_Click(object sender, EventArgs e)
        {
            if (_virtualizationGestor != null)
                _virtualizationGestor.Hide();
            tabControlRecherches.Show();
        }

        private void sélectionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listView1.Items.Count > 0 &&
                listView1.SelectedItems.Count > 0)
                ExcelExportWrapper.ExportSelectionToExcel(listView1.SelectedItems);
        }

        private void lensembleDesRésultatsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listView1.Items.Count > 0 &&
               listView1.SelectedItems.Count > 0)
                ExcelExportWrapper.ExportSelectionToExcel(listView1.Items);
        }
    }
}
